/***********************************************************************************
 * 文件名： mcu_adc.h
 * 版本： 1
 * 说明：
 * 		NY8 的ADC外设驱动。使用说明见头文件。在 ny8_driver_config.h 文件中可配置初始状态。
 *  ADC驱动不包括IO口的模式设置，使用前需要自行确定IO口模式是否为模拟模式。
 * 修改记录：
 * 	2022/04/13: 初版。 刘言。
 *  2022/06/14: 失能ADC后再使能执行丢弃，
***********************************************************************************/
#ifndef _MCU_ADC_H
#define _MCU_ADC_H

#include "mcu.h"

#define ADC_DISCARD_NUM     8   // 切换通道之后需要抛弃的笔数

// ADC满量程定义
#if (ADC_DLEN == 0)
    #define ADC_FULL_VALUE  255
#else
    #define ADC_FULL_VALUE  4095
#endif

// ADC特殊通道定义

// 1/4VDD通道
#define ADC_CH_VDD      11

// 参考电压
typedef enum _adc_vref
{
    ADC_VREF_2V = 0,
    ADC_VREF_3V = 1,
    ADC_VREF_4V = 2,
    ADC_VREF_VDD = 3,
    ADC_VREF_EXT = 0X80
}adc_vref_t;

//! PCON (Power Control Register)
/**
 * |---------------+-------+------+-------+--------+-------+------+------+------|
 * |               | Bit7  | Bit6 | Bit5  | Bit4   | Bit3  | Bit2 | Bit1 | Bit0 |
 * |---------------+-------+------+-------+--------+-------+------+------+------|
 * |               | WDTEN | GP6  | LVDEN | /PHPA5 | LVREN | GP2  | GP1  | GP0  |
 * |---------------+-------+------+-------+--------+-------+------+------+------|
 * | R/W Property  |                              R/W                           |
 * |---------------+-------+------+-------+--------+-------+------+------+------|
 * | Initial Value | 1     | 0    | 0     | 1      | 1     | 0    | 0    | 0    |
 * |---------------+-------+------+-------+--------+-------+------+------+------|
 *  PCON.GP0 已被ADC驱动使用。
 */

#if (ADC_DISCARD_NUM > 0)
#define _Adc_mRunning   PCONbits.GP0    // 抛弃过程中要始终标记ADC在运行
extern u8 _Adc_mDiscard;        // 抛弃计数
#endif


#if (ADC_DISCARD_NUM > 0)
/* AD转换完成事件，必须在ADC中断中调用
void isr_hw(void) __interrupt(0)
{
    ...
    if (Adc_GetIntFlag())
    {
        Adc_EvComplated(Adc_GetResult());
        Adc_ClrIntFlag();
    }
    ...
}*/
#define Adc_EvComplated(adv)     \
{	\
	if (_Adc_mDiscard < ADC_DISCARD_NUM) /*需要丢弃*/	\
    {	\
		_Adc_mDiscard++;	\
        START = 1;	/* 继续转换 */	\
    }	\
	else	\
	{	\
		_Adc_mRunning = false;	\
		Adc_EventDone(adv);		/* 注意：这里可能重新Start, start后Adc_mRunning为true */\
	}\
}

// [异步]启动AD转换,未等待转换完毕。
// u8_ch : 通道号，取值范围 0~11
// 转换完成后触发 Adc_EventDone。
// 注意：确保已使能ADC；得到结果通知前请不要再调用启动任何转换任务。
void Adc_Start(u8 ch);

// 查询ADC是否繁忙（正在转换）
#define Adc_IsBusy()    _Adc_mRunning

/**
  \brief   设置ADC参考电压
  \details 
  \param [in]   adc_vref_t_vref  取值见 adc_vref_t
  \return   无
 */
void Adc_SetVref(adc_vref_t vref);
#else
/* AD转换完成事件，必须在ADC中断中调用
void isr_hw(void) __interrupt(0)
{
    ...
    if (Adc_GetIntFlag())
    {
        Adc_EvComplated(Adc_GetResult());
        Adc_ClrIntFlag();
    }
    ...
}*/
#define Adc_EvComplated(adv)     Adc_EventDone(adv)

// [异步]启动AD转换,未等待转换完毕。
// u8_ch : 通道号，取值范围 0~11
// 转换完成后触发 Adc_EventDone。
// 注意：确保已使能ADC；得到结果通知前请不要再调用启动任何转换任务。
#define Adc_Start(u8_ch)    \
{\
    /*ADMD &= 0XF0;*/\
    /*ADMD |= u8_ch;*/\
    ADMD = 0X90|u8_ch; \
    START = 1;\
}

// 查询ADC是否繁忙（正在转换）
#define Adc_IsBusy()    (!EOC)

/**
  \brief   设置ADC参考电压
  \details 
  \param [in]   adc_vref_t_vref  取值见 adc_vref_t
  \return   无
 */
#define Adc_SetVref(adc_vref_t_vref)    ADVREFH = adc_vref_t_vref
#endif

// ADC初始化
// 按照配置文件中的初始参数进行初始化。注意中断初始状态在 Mcu_Init 中已统一初始化。
#define Adc_Init()  \
{\
    ADR = (ADC_ADCK << 4) \
            |(1 << 6); /*AD中断默认开启*/\
    ADCR = (2 << 0)   /*固定12位*/ \
            |(ADC_SHCK << 2);\
    ADVREFH = (ADC_EVHENB << 7) \
            |(ADC_VHS << 0);\
    ADMD = (ADC_EN << 7) \
            |(1 << 4) /*GCHS=1*/\
            |(ADC_CH << 0);\
    /*必须等待256us后ADC电路稳定*/\
}



// 获取现在的通道号
#define Adc_GetCH()         (ADMD&0x0F)

// 获取现在的参考电压
#define Adc_GetVref()       ADVREFH

// 获取最后一次转换结果
#if (ADC_DLEN == 0)
    #define Adc_GetResult()     ADD
#else
    #define Adc_GetResult()     (((u16)ADD << 4) + (ADR & 0x0F))
#endif

// 获取中断标志
#define Adc_GetIntFlag()    ADIF

// 清除中断标志
#define Adc_ClrIntFlag()    ADIF = 0

// 使能ADC
#define Adc_Enable()    {ADEN = 1;}

// 失能ADC，可以省电
#define Adc_Disable()   {ADEN = 0; _Adc_mDiscard = 0;}

#if (ADC_DLEN == 0)
// 启动一次AD转换，并返回结果
// 不会触发 Adc_EventDone (转换时临时关闭了ADC中断)。
// 注意：正在进行异步转换任务时请勿调用本函数，否则该次异步转换任务会被作废，不会有转换完成通知，且获取不可靠的结果。
u8 Adc_GetOnce(u8 ch);
#else
// 启动一次AD转换，并返回结果
// 不会触发 Adc_EventDone (转换时临时关闭了ADC中断)。
// 注意：正在进行异步转换任务时请勿调用本函数，否则该次异步转换任务会被作废，不会有转换完成通知，且获取不可靠的结果。
u16 Adc_GetOnce(u8 ch);
#endif



#endif
